/*
 Xern Entity Relocation Network
 Copyright (C) 2010-2011 Ronie Salgado <roniesalg@gmail.com>

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
*/

#include <stdio.h>
#include <squirrel.h>
//#include <sqstdio.h>
#include "Xern/FileSystem.hpp"
#include "InputOutput.hpp"
#include "SquirrelScriptEngine.hpp"

#ifdef SQUNICODE
#include <string>
#include "utf8.h"
#include "Xern/StringUtils.hpp"
#endif

namespace XernSquirrel
{

	SQInteger _std_loadfile(HSQUIRRELVM vm)
	{
		std::string path;
		SQBool printerror = SQFalse;
		const SQChar *spath;
		sq_getstring(vm, 2, &spath);
		if(sq_gettop(vm) >= 3)
			sq_getbool(vm, 3, &printerror);

#ifdef SQUNICODE
		if(spath)
		{
			std::wstring wpath = spath;
			path = wstring2string(wpath)
		}
#else
		if(spath)
			path = spath;
#endif
		return SquirrelScriptEngine::Get().ioReplace.LoadFile(vm, path, printerror);
	}

	SQInteger _std_dofile(HSQUIRRELVM vm)
	{
		std::string path;
		SQBool printerror = SQFalse;
		const SQChar *spath;
		sq_getstring(vm, 2, &spath);
		if(sq_gettop(vm) >= 3)
			sq_getbool(vm, 3, &printerror);

#ifdef SQUNICODE
		if(spath)
		{
			std::wstring wpath = spath;
			path = wstring2string(wpath)
		}
#else
		if(spath)
			path = spath;
#endif
		return SquirrelScriptEngine::Get().ioReplace.DoFile(vm, path, printerror);
	}

	static SQInteger _noop(HSQUIRRELVM v)
	{
		return 0;
	}

	InputOutput::InputOutput()
	{
	}

	InputOutput::~InputOutput()
	{
	}

	void InputOutput::RegisterLibrary(HSQUIRRELVM vm)
	{
		virtualMachine = vm;
		sq_pushroottable(vm);

		sq_pushstring(vm, _SC("loadfile"), -1);
		sq_newclosure(vm, _std_loadfile, 0);
		sq_rawset(vm, -3);

		sq_pushstring(vm, _SC("dofile"), -1);
		sq_newclosure(vm, _std_dofile, 0);
		sq_rawset(vm, -3);
		sq_pop(vm, 1);
	}

	SQInteger InputOutput::LoadFile(HSQUIRRELVM v, const std::string &path, bool printerror)
	{
		// Check the path.
		if(path.empty())
			return sq_throwerror(v, _SC("trying to load file with empty path."));

		// Try first with the raw path.
		FILE *file = NULL;
		std::string realpath;
		if(IsFile(path))
		{
			// Open the file.
			file = fopen(path.c_str(), "rb");
			realpath = path;
		}
		else
		{
			// Then with the expanded path.
			realpath = JoinPath(currentFolder, path);
			if(!IsFile(realpath))
				return sq_throwerror(v, _SC("tried to load an inexistent file."));
			file = fopen(realpath.c_str(), "rb");
		}

		// Check if the file could be opened.
		if(!file)
			return sq_throwerror(v, _SC("failed to open the file."));

		// Get the file size.
		fseek(file, 0, SEEK_END);
		size_t len = ftell(file);
		fseek(file, 0, SEEK_SET);

		// If the file is empty, return an empty closure.
		if(len == 0)
		{
			fclose(file);
			sq_newclosure(v, _noop, 0);
			return 1;
		}

		// Just load all of the file into the memory.
		char *data = new char[len];
		size_t readed = fread(data, len, 1, file);

		// Close the file.
		fclose(file);

		// Check that the file was readed.
		if(readed != 1)
		{
			delete [] data;
			return sq_throwerror(v, _SC("failed to read the file"));
		}

		// TODO: Check the hash and cache the file.

		// Compile the file.
		const SQChar *code, *sourcename;
		size_t codelen;
#ifdef SQUNICODE
		std::wstring wcode = string2wstring(data);
		std::wstring wpath = string2wstring(realpath);
		code = wcode.c_str();
		codelen = wcode.size();
#else
		code = data;
		codelen = len;
		sourcename = realpath.c_str();
#endif
		if(SQ_SUCCEEDED(sq_compilebuffer(v, code, len, sourcename, printerror)))
		{
			delete [] data;
			return 1;
		}
		delete [] data;
		return SQ_ERROR;
	}

	SQInteger InputOutput::DoFile(HSQUIRRELVM v, const std::string &path, bool printerror)
	{
		// Load the file first.
		if(!SQ_SUCCEEDED(LoadFile(v, path, printerror)))
			return SQ_ERROR;

		// Update the store the path and set the new.
		std::string oldpath = currentFolder;
		currentFolder = DirName(path);
		if(!IsDirectory(currentFolder))
			currentFolder = JoinPath(oldpath, currentFolder);
		if(currentFolder == ".")
			currentFolder = oldpath;

		// Push the environment.
		sq_push(v, 1);

		// Call the file.
		if(SQ_SUCCEEDED(sq_call(v, 1, SQTrue, SQTrue)))
		{
			// Restore the path.
			currentFolder = oldpath;
			return 1;
		}

		// Restore the path.
		currentFolder = oldpath;

		sq_pop(v, 1);
		return SQ_ERROR;
	}
};
